local require = require
local log    = require("modules.commhm.log")
local utils  = require("modules.commhm.utils")
local config = require("modules.business_common.business_config")
local http   = require("modules.business_common.utils.business_http")
local auth_md5  = require("modules.business_common.auth.md5")

local ngx = ngx
local type = type
local next = next
local pairs = pairs
local ipairs = ipairs
local tonumber = tonumber
local math = math
local math_abs = math.abs
--local string2table = table.loadstring
local function string2table(s_)
	local f_ = loadstring('do local ret=' .. s_ .. ' return ret end')
	if f_ then
		return f_()
	end
end

local FOO = {
    _VERSION = "0.0.1",
    DEFAULT_REASON = 'from business_mgr',
    ACCOUNT_SEED = '522e6ccde68f4584a3282ce0b0eca1cf',

    -- 支付方式
    PAYMENTS = {
        MINIBEAN = 0, -- 迷你豆支付
        MINIDIAN = 1, -- 迷你点支付
        MINICOIN = 2, -- 迷你币支付
        STARCOIN = 3, -- 星星币支付
        FREECOIN = 4, -- 免费广告
        INVALID = 5
    },

    -- gmcost 已知的返回值
    RESULT = {
        OVERLOAD = 'OVERLOAD',  -- 过载
        SUCCESS  = 'SUCCESS',   -- 成功
    },

    OPTS = {
        RETRY = true    -- 重试
    },
}

-- 支付方式对应的物品id
FOO.PAYMENT_ITEM = {
    [FOO.PAYMENTS.MINIBEAN] = 10000,   -- 迷你豆
    [FOO.PAYMENTS.MINIDIAN] = 10009,   -- 迷你点
    [FOO.PAYMENTS.MINICOIN] = 10002,   -- 迷你币
}

local successful = function (response)
    if not response or response.return_code ~= FOO.RESULT.SUCCESS then
        return false
    end

    return true
end


--不适用批量处理业务中,return_code为失败,但是在response.return_data的多业务处理中有成功的返回
local http_request = function(uin, request, retry, _server)
    if not request or not (request.cmd or request.act) then
        return nil
    end

    request.uin  = uin
    request.time = utils.now()
    request.encrypt_ver = auth_md5.ENCRYPT.MD5_VER2
    if request.act then
        request.md5 = auth_md5.make_md5_v2(request)
    else
        request.md5 = ngx.md5(request.time .. "c6f89d107f56eede" .. uin)
    end

    local server = _server
    if server == nil then
        server = config.get_asset_server_url(uin)
    end
    local result= http.Get(server, request)
    local response = string2table(result or '')
    -- 过载重试
    -- if retry and response and response.data.return_code == FOO.RESULT.OVERLOAD then
    --     response = string2table(http.Get(server, request) or '')
    -- end
    -- 失败日志
    if not response or not response.ret then http.Log(utils.to_str({
        server   = server,
        request  = request,
        response = response
    })) end
    return response
end

local payfunc = {
    [FOO.PAYMENTS.MINIBEAN] = FOO.minibean_remove,
    [FOO.PAYMENTS.MINIDIAN] = FOO.minidian_remove,
    [FOO.PAYMENTS.MINICOIN] = FOO.minicoin_remove,
    [FOO.PAYMENTS.STARCOIN] = function(...) return true end,
    [FOO.PAYMENTS.FREECOIN] = function(...) return true end
}

-- !@ 迷你币
FOO.minicoin_remove = function (uin, num, approach, reason, ext)
    num = tonumber(num) or 0
    if not uin or num == 0 then
        return false
    end

    if not approach or not reason then
        log.day_list("asset_mgr", "minidian_remove", "aporoach or reason is nil!", uin, approach or "nil" , reason or "nil")
        log.debug("minidian_remove aporoach or reason is nil!")
        return false
    end

    local result = http_request(uin, {
        cmd      = "minicoin_rm",       --减迷你币
        minicoin = math_abs(num),
        reason   = reason,
        approach = approach,
        ext      = type(ext) == "table" and utils.table2json(ext) or ext,
    }, FOO.OPTS.RETRY)

    if not result then
        log.day_list('business_asset', "minicoin_remove", "call failed", uin, approach, reason, num)
        return false
    end
    return successful(result.data) and true or false
end


-- !@ 迷你点
FOO.minidian_remove = function (uin, num, approach, reason, ext)
    num = tonumber(num) or 0
    if not uin or num == 0 then
        return false
    end

    if not approach or not reason then
        log.day_list("asset_mgr", "minidian_remove", "aporoach or reason is nil!", uin, approach or "nil" , reason or "nil")
        log.debug("minidian_remove aporoach or reason is nil!")
        return false
    end

    local result = http_request(uin, {
        cmd      = "adpoint_rm",       --减迷你点
        num      = math_abs(num),
        reason   = reason,
        approach = approach,
        ext      = type(ext) == "table" and utils.table2json(ext) or ext,
    }, FOO.OPTS.RETRY)

    if not result then
        log.day_list('business_asset', "minidian_remove", "call failed", uin, approach, reason, num)
        return false
    end
    return successful(result.data) and true or false
end

-- !@ 迷你豆
FOO.minibean_remove = function (uin, num, approach, reason, ext)
    num = tonumber(num) or 0
    if not uin or num == 0 then
        return false
    end

    if not approach or not reason then
        log.day_list("asset_mgr", "minidian_remove", "aporoach or reason is nil!", uin, approach or "nil" , reason or "nil")
        log.debug("minidian_remove aporoach or reason is nil!")
        return false
    end

    local result = http_request(uin, {
        cmd      = "minibean_rm",       --减迷你豆
        minibean = math_abs(num),
        reason   = reason,
        approach = approach,
        ext      = type(ext) == "table" and utils.table2json(ext) or ext,
    }, FOO.OPTS.RETRY)

    if not result then
        log.day_list('business_asset', "minibean_remove", "call failed", uin, approach, reason, num)
        return false
    end
    return successful(result.data) and true or false
end

-- !@ 货币支付
FOO.defray = function(uin, price_type, price_num, approach, reason, ext)
    return payfunc[price_type] and payfunc[price_type](uin, price_num,approach, reason, ext) or false
end

-- !@ 货币返还
FOO.refund = function (uin, price_type, price_num, approach, reason)
    local result = true
    local itemid = FOO.PAYMENT_ITEM[price_type]
    if itemid then

        -- 按配置返回道具
        local item_refund = {{id = itemid, num = price_num}}
        result = FOO.item_add(uin, item_refund, approach, reason)
    end

    log.day_list("asset_mgr", "refund_item", utils.to_str({
        uin    = uin,
        type   = price_type,
        num    = price_num,
        reason = reason,
        result = result
    }))

    return result
end

--! @brief 扣除物品
FOO.item_remove = function (uin, itemid, num, approach, reason, ext)
    itemid = tonumber(itemid) or 0
    num    = tonumber(num)    or 0
    if itemid == 0 or num == 0 then
        log.day_list("asset_mgr", "item_remove", "id or num is nil", uin, approach or "nil" , reason or "nil", utils.to_str({id=itemid, num=num}))
        return false
    end

    if not approach or not reason then
        log.day_list("asset_mgr", "item_remove", "aporoach or reason is nil!", uin, approach or "nil" , reason or "nil", utils.to_str({id=itemid, num=num}))
        log.debug("item_remove aporoach or reason is nil!")
        return false
    end

    local result = http_request(uin, {
        cmd    = "item_rm",
        id     = itemid,
        num    = math_abs(num),
        reason = reason,
        approach = approach,
        ext    = type(ext) == "table" and utils.table2json(ext) or ext,
        encrypt_ver = auth_md5.ENCRYPT.MD5_VER2,
    }, FOO.OPTS.RETRY, config.get_asset_server_url(uin))

    if not result then
        log.day_list('business_asset', "item_rm", "call failed", uin, reason, itemid, num)
        return false
    end
    return successful(result.data) and true or false
end

FOO.items_remove = function (uin, items, approach , reason, ext)
    if not uin then
        return false
    end

    local bauto_ext = false
    if not ext then
        bauto_ext = true
    end

    local items_ = {}
    for _, item in pairs(items or {}) do
        local id  = tonumber(item.id)  or 0
        local num = tonumber(item.num) or 0

        if id > 0 and num > 0 then
            items_[#items_+1] = {id=id, num=num}
        else
            log.day_list('business_asset', "items_remove", "id or num is nil", uin, reason, utils.to_str(items))
        end

        if bauto_ext then
            ext = ext or {}
            ext[#ext+1] = {item_id = item.id,item_num = item.num,item_type = 0, item_price=  0}
        end
    end

    if not next(items_) then
        log.day_list('business_asset', "items_remove", "items is nil", uin, approach or "nil" , reason or "nil", utils.to_str(items))
        return false
    end

    if not approach or not reason then
        log.day_list("asset_mgr", "item_add", "aporoach or reason is nil!", uin, approach or "nil" , reason or "nil", utils.to_str(items))
        log.debug("item_remove aporoach or reason is nil!")
        return false
    end

    local result = http_request(uin, {
        cmd      ='items_rm',
        items    = utils.table2json(items_),
        reason   = reason,
        approach = approach,
        ext      = type(ext) == "table" and utils.table2json(ext) or ext,
        encrypt_ver = auth_md5.ENCRYPT.MD5_VER2,
    }, FOO.OPTS.RETRY)

    if not result then
        log.day_list('business_asset', "items_remove", "call failed", uin, reason, utils.to_str(items))
        return false
    end
    return successful(result.data) and true or false
end

--! @brief 新增物品
--! @param items: {{id=xx,num=xx},{id=xx,num=xx},...}
--! @param exts : {{item_id=xx,item_num=xx,item_type=xx,item_price=xx},...}
FOO.item_add = function (uin, items, approach, reason, ext)
    if not uin then
        return false
    end

    local bauto_ext = false
    if not ext then
        bauto_ext = true
    end

    local items_ = {}
    for _, item in pairs(items or {}) do
        local id  = tonumber(item.id)  or 0
        local num = tonumber(item.num) or 0

        if id > 0 and num > 0 then
            items_[#items_+1] = {id=id, num=num}
        else
            log.day_list('business_asset', "item_add", "id or num is nil", uin, reason, utils.to_str(items))
        end

        if bauto_ext then
            ext = ext or {}
            ext[#ext+1] = {item_id = item.id,item_num = item.num,item_type = 0, item_price=  0}
        end
    end

    if not next(items_) then
        log.day_list('business_asset', "item_add", "items is nil", uin, approach or "nil" , reason or "nil", utils.to_str(items))
        return false
    end

    if not approach or not reason then
        log.day_list("asset_mgr", "item_add", "aporoach or reason is nil!", uin, approach or "nil" , reason or "nil", utils.to_str(items))
        log.debug("item_remove aporoach or reason is nil!")
        return false
    end

    local result = http_request(uin, {
        cmd      ='item_add',
        items    = utils.table2json(items_),
        reason   = reason,
        approach = approach,
        ext      = type(ext) == "table" and utils.table2json(ext) or ext,
        encrypt_ver = auth_md5.ENCRYPT.MD5_VER2,
    }, FOO.OPTS.RETRY)

    if not result then
        log.day_list('business_asset', "item_add", "call failed", uin, reason, utils.to_str(items))
        return false
    end
    return result.data
end



FOO.add_skin = function (skinid, reason, uin, approach)
    uin = uin or ngx.ctx.m_params.uin
    local result = http_request(uin, {
        cmd      = "skin_add",
        reason   = reason,
        SkinID   = skinid,
        ExpireTimeType = -1,
        approach = approach,
    })

    if not result then
        log.day_list('business_asset', "add_skin", "call failed", uin, reason, skinid)
        return false
    end

    return result.ret, result.data
end

FOO.remove_skin = function(skinid, reason, uin, approach)
    uin = uin or ngx.ctx.m_params.uin
    reason = reason or "business_cmd"
    local result = http_request(uin,  {
        cmd = 'remove_skin',
        SkinID = skinid,
        reason = reason,
        approach = approach,
    })

    if not result then
        log.day_list('business_asset', "remove_skin", "call failed", uin, reason, skinid)
        return false
    end

    return result.ret, result.data
end


FOO.add_mount = function (mountid, level, reason, uin, approach)
    uin = uin or ngx.ctx.m_params.uin
    local result = http_request(uin, {
        cmd        = "add_mount",
        reason     = reason,
        RiderID    = mountid,
        RiderLevel = level,
        approach   = approach,
    })

    if not result then
        log.day_list('business_asset', "add_mount", "call failed", uin, reason, mountid, level, approach)
        return false
    end

    return result.ret, result.data
end



FOO.add_avatar = function (modelid, reason, dst_uin, src_uin, approach)
    dst_uin = dst_uin or ngx.ctx.m_params.uin
    src_uin = src_uin or dst_uin
    local result = http_request(dst_uin, {
        cmd            = "add_avatar",
        reason         = reason,
        approach = approach,
        modelid        = modelid,
        author_uin     = 1000,
        ExpireTimeType = -1,
    })

    if not result then
        log.day_list('business_asset', "add_avatar", "call failed", dst_uin, src_uin, reason, modelid,  approach)
        return false
    end
    return result.ret, result.data
end


return FOO
